package com.bridgeintelligent.tag.webserver.dim.query.service;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.bridgeintelligent.tag.utils.DateHelper;
import com.bridgeintelligent.tag.webserver.dim.query.api.ThemeDetailResult;
import com.bridgeintelligent.tag.webserver.dim.query.api.ThemeQueryModel;
import com.bridgeintelligent.tag.webserver.dim.query.api.ThemeToResutl;
import com.bridgeintelligent.tag.webserver.dim.query.service.thread.ThemeThreadPoolExecutor;
import com.bridgeintelligent.tag.webserver.system.entity.DimUseRecord;
import com.bridgeintelligent.tag.webserver.system.service.DimUseRecordService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wayneleo.quickstart.framework.IDGenerator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static javax.servlet.http.HttpServletResponse.SC_OK;

@Api(tags = "主题明细excel导出")
@AllArgsConstructor
@RestController
@Slf4j
@RequestMapping(value = "/api/themeDetail")
public class ThemeDetailExportController {

    private  QueryApiUtils queryApiUtils;

    private static final Integer SIZE = 10000;

    //每个sheet最多写一百万条数据 保持ROW_SIZE、WRITE_SIZE整除，减少判断
    private static final Integer ROW_SIZE = 500000;

    //每次写sheet最多写二十万(这里es每次最大为10000)
    private static final Integer WRITE_SIZE = 10000;

    private ObjectMapper objectMapper;

    private static final String CUST_NO = "cust_no";

    private ThemeThreadPoolExecutor themeThreadPoolExecutor;

    private DimUseRecordService recordService;


    @ApiOperation(value = "主题明细-新版导出Excel", notes = "动态导出")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "condition", value = "查询条件", required = true)
    })
    @PostMapping("/export")
    public void export1(String condition,HttpServletRequest request, HttpServletResponse response) throws IOException {

        OutputStream outputStream =null;
        try{

            ThemeQueryModel themeQueryModel = objectMapper.readValue(condition, ThemeQueryModel.class);
            try {
                if (CollUtil.isNotEmpty(themeQueryModel.getFields())){
                    String dims = themeQueryModel.getFields().stream().collect(Collectors.joining(","));
                    recordService.addRecord(DimUseRecord.of().setId(IDGenerator.gen()).setDataType("1").setDimId(dims).setCreateTime(DateHelper.currentDateTime()));
                }
            }catch (Exception e){
                log.error("保存标签下载记录异常",e);
            }

            String fileName = themeQueryModel.getThemeName()+"明细-"+ DateFormatUtils.format(new Date(),"yyyy-MM-dd");
            fileName = new String(fileName.getBytes(),"UTF-8");

            themeQueryModel.setSize(SIZE);
            List<String> fields = themeQueryModel.getFields();
            //获取动态字段中文名称 <"age","年龄">
            Map<String, String> fieldMap = themeQueryModel.getFieldMap();
            List<List<String>> head = new ArrayList<List<String>>();
            for (int i = 0; i < fields.size(); i++) {
                head.add(Arrays.asList(fieldMap.get(fields.get(i))));
            }

            //先去查询该主题客群总数totalCount
            QueryApi queryApi = queryApiUtils.build(QueryApi.class);
            ThemeDetailResult result = queryApi.themeCount(JSON.toJSONString(themeQueryModel));
            if (result != null && result.getCode() == 0){

                int from = 0;

                int pageNum = 1;

                int totalCount = result.getTotalCount();

                //每一个Sheet存放100w条数据
                int sheetDataRows = ROW_SIZE;
                //每次写入的数据量20w,每页查询20W
                int writeDataRows = WRITE_SIZE;
                //计算需要的Sheet数量
                Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);

                //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
                Integer oneSheetWriteCount = sheetDataRows / writeDataRows;

                //计算最后一个sheet需要写入的次数
                Integer lastSheetWriteCount = totalCount % sheetDataRows  == 0 ? (sheetDataRows / writeDataRows)
                        : ( totalCount % sheetDataRows % writeDataRows == 0 ?  ((totalCount % sheetDataRows ) / writeDataRows)
                        : ((totalCount % sheetDataRows ) / writeDataRows)+1);

                log.info("sheetNum:{},oneSheetWriteCount:{},lastSheetWriteCount:{}",sheetNum,oneSheetWriteCount,lastSheetWriteCount);
                outputStream = response.getOutputStream();
                //必须放到循环外，否则会刷新流
                String filepath = "/tmp/" + System.currentTimeMillis() + ".xlsx";
                File file = new File( filepath );
                // ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
                ExcelWriter excelWriter = EasyExcel.write(file).build();
                //开始分批查询分次写入

                themeThreadPoolExecutor.handler(sheetNum,ROW_SIZE,oneSheetWriteCount,lastSheetWriteCount,
                        head,themeQueryModel,WRITE_SIZE,queryApi,excelWriter);
                //CountDownLatch countDownLatch = new CountDownLatch(sheetNum);
                /*for (int i = 0; i < sheetNum; i++) {
                    ThemeWriteThread themeWriteThread = new ThemeWriteThread(countDownLatch,i,sheetNum,oneSheetWriteCount,lastSheetWriteCount,head,themeQueryModel,WRITE_SIZE,queryApi,excelWriter);
                    executor.execute(themeWriteThread);

                    //创建Sheet
                    WriteSheet sheet = new WriteSheet();
                    sheet.setSheetName("Sheet"+i);
                    sheet.setSheetNo(i);
                    //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                    for (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                        WriteSheet writeSheet = EasyExcel.writerSheet(i, "Sheet" + (i + 1)).head(head)
                                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
                        themeQueryModel.setFrom(from);
                        result =  queryApi.themeDetail(JSON.toJSONString(themeQueryModel));
                        if (result != null && result.getCode() == 0 && result.getData() != null){
                            excelWriter.write(handResult(themeQueryModel,result.getData().getContent()), writeSheet);
                        }
                        from = pageNum*SIZE;
                        pageNum = pageNum+1;
                    }

                }*/

                // 下载EXCEL，返回给前段stream流
                response.setContentType("application/octet-stream");
                response.setCharacterEncoding("utf-8");
                response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()) + ".xlsx");
                excelWriter.finish();
                FileInputStream in = new FileInputStream( filepath );
                byte[] b = new byte[512 * 1024];
                int len;
                while ( ( len = in.read(b) ) > 0 ) {
                    outputStream.write( b, 0, len );
                }
                in.close();
                // outputStream.flush();
                // outputStream.close();
                log.info("*********导出结束！**************");
            }
        }catch (Exception e){
            log.error("主题明细导出异常", e);
        }finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }

    }

    public static void main(String[] args) {
        int totalCount = 100001;
        int sheetDataRows = 10000;
        int writeDataRows = 1000;
        Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
        Integer oneSheetWriteCount = sheetDataRows / writeDataRows;

        Integer lastSheetWriteCount = totalCount % sheetDataRows  == 0 ? (sheetDataRows / writeDataRows)
                : ( totalCount % sheetDataRows % writeDataRows == 0 ?  ((totalCount % sheetDataRows ) / writeDataRows)
                : ((totalCount % sheetDataRows ) / writeDataRows)+1);
        System.out.println("sheetNum:{},oneSheetWriteCount:{},lastSheetWriteCount:{}"+sheetNum+"/"+oneSheetWriteCount+"/"+lastSheetWriteCount);
        System.out.println(totalCount / sheetDataRows / writeDataRows);

        // i= 0   from  0
        // i = 1  from =  1 * sheetDataRows
        // i = 2  from  =  2 * sheetDataRows
        //  i = 9  from  = 9 *  sheetDataRows

    }


    @ApiOperation(value = "主题明细-导出Excel", notes = "动态导出")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "condition", value = "查询条件", required = true)
    })
    @PostMapping("/export1")
    public void export(String condition,HttpServletRequest request, HttpServletResponse response) throws IOException {
        try{
            ThemeQueryModel themeQueryModel = objectMapper.readValue(condition, ThemeQueryModel.class);
            String themeName = themeQueryModel.getThemeName();
            themeQueryModel.setFrom(0).setSize(SIZE);
            QueryApi queryApi = queryApiUtils.build(QueryApi.class);
            ThemeDetailResult result =  queryApi.themeDetail(JSON.toJSONString(themeQueryModel));

            XSSFWorkbook wb=new XSSFWorkbook();
            CellStyle commonStyle = setStyle(wb);

            if (result != null && result.getCode() == 0){
                createThemeDetailSheet(wb, commonStyle ,result,themeQueryModel);
            }
            // 创建文档信息
            try(OutputStream os=response.getOutputStream()) {
                response.reset();
                response.setStatus(SC_OK);
                String fileName = themeName+"明细-"+ DateFormatUtils.format(new Date(),"yyyy-MM-dd");
                fileName = new String(fileName.getBytes(),"UTF-8");
                response.setHeader("Content-disposition", String.format("attachment; filename=\"%s.xlsx\"", fileName));
                response.setContentType("application/msexcel");
                wb.write(os);
                os.flush();
            } catch (IOException e) {
                log.error(themeName+"明细导出异常", e);
                throw new RuntimeException(themeName+"明细导出异常");
            }
        }catch (Exception e){
            log.error("主题明细导出异常", e);
            throw new RuntimeException("主题明细导出异常");
        }

    }

    public void createThemeDetailSheet(XSSFWorkbook wb, CellStyle commonStyle ,ThemeDetailResult result,ThemeQueryModel queryModel) {
        try {

            // 创建sheet
            Sheet sheet = wb.createSheet(queryModel.getThemeName());

            List<String> fields = queryModel.getFields();
            //获取动态字段中文名称 <"age","年龄">
            Map<String, String> fieldMap = queryModel.getFieldMap();

            //设置第1行title
            int rowNum = 0;
            //设置列宽
            for (int i = 0; i < fields.size() + 2; i++) {
                sheet.setColumnWidth(i, 3000);
            }

            Row r0 = sheet.createRow(rowNum++);
            r0.setHeight((short) 700);
            for (int i = 0; i < fields.size(); i++) {
                Cell tempCell = r0.createCell(i);
                tempCell.setCellValue(fieldMap.get(fields.get(i)));
                tempCell.setCellStyle(commonStyle);
            }
            //处理数据
            ThemeDetailResult.R data = result.getData();
            if (data != null && data.getTotalPage() >= 1){
                rowNum = writeData(commonStyle,sheet,rowNum,fields, data.getContent(),queryModel.getFieldMap());
                for (int i = 2; i <= data.getTotalPage() ; i++) {
                    queryModel.setFrom((i-1)*SIZE);
                    QueryApi queryApi = queryApiUtils.build(QueryApi.class);
                    result =  queryApi.themeDetail(JSON.toJSONString(queryModel));
                    if (result != null && result.getCode() == 0 && result.getData() != null){
                        rowNum = writeData(commonStyle,sheet,rowNum,fields,result.getData().getContent(),queryModel.getFieldMap());
                    }

                }
            }
        } catch (Exception e) {
            log.error("导出" + queryModel.getThemeName() + "时出现异常", e);
        }
    }

   private int writeData(CellStyle commonStyle,Sheet sheet,int rowNum,List<String> fields,List<Map<String, String>> content,Map<String,String> fieldMap){
       if (CollectionUtils.isNotEmpty(content)){
           for (Map<String, String> map : content) {
               Row row = sheet.createRow(rowNum++);
               for (int i = 0; i < fields.size(); i++) {
                   //列表数据
                   Cell tempCell1 = row.createCell(i);
                   if (fields.get(i).equals(CUST_NO)){
                       tempCell1.setCellValue(map.get(fields.get(i)));
                   }else{
                       tempCell1.setCellValue(map.get(fieldMap.get(fields.get(i))));
                   }
                   tempCell1.setCellStyle(commonStyle);
               }
           }
       }
       return rowNum;

   }

    public CellStyle setStyle(XSSFWorkbook wb) {
        //表头字体
        Font headerFont = wb.createFont();
        headerFont.setFontName("宋体");
        headerFont.setFontHeightInPoints((short) 18);
        headerFont.setBold(true);
        headerFont.setColor(Font.COLOR_NORMAL);
        //正文字体
        Font contextFont = wb.createFont();
        contextFont.setFontName("宋体");
        contextFont.setFontHeightInPoints((short) 12);
        headerFont.setBold(true);
        //表头样式，左右上下居中
        CellStyle headerStyle = wb.createCellStyle();
        headerStyle.setFont(headerFont);
        // 左右居中
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        // 上下居中
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headerStyle.setLocked(true);
        // 自动换行
        headerStyle.setWrapText(false);
        //单元格样式，左右上下居中 边框
        CellStyle commonStyle = wb.createCellStyle();
        commonStyle.setFont(contextFont);
        // 左右居中
        commonStyle.setAlignment(HorizontalAlignment.CENTER);
        // 上下居中
        commonStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        commonStyle.setLocked(true);
        // 自动换行
        commonStyle.setWrapText(false);
        //单元格样式，左右上下居中 边框
        CellStyle commonWrapStyle = wb.createCellStyle();
        commonWrapStyle.setFont(contextFont);
        //单元格样式，竖向 边框
        CellStyle verticalStyle = wb.createCellStyle();
        verticalStyle.setFont(contextFont);
        CellStyle commonStyleNoBorder = wb.createCellStyle();
        commonStyleNoBorder.setFont(contextFont);
        commonStyleNoBorder.setLocked(true);
        // 自动换行
        commonStyleNoBorder.setWrapText(false);
        CellStyle alignLeftStyle = wb.createCellStyle();
        alignLeftStyle.setFont(contextFont);
        alignLeftStyle.setLocked(true);
        // 自动换行
        alignLeftStyle.setWrapText(false);
        //单元格样式，左对齐 无边框
        CellStyle alignLeftNoBorderStyle = wb.createCellStyle();
        alignLeftNoBorderStyle.setFont(contextFont);
        alignLeftNoBorderStyle.setLocked(true);
        // 自动换行
        alignLeftNoBorderStyle.setWrapText(false);
        //单元格样式，右对齐
        CellStyle alignRightStyle = wb.createCellStyle();
        alignRightStyle.setFont(contextFont);
        alignRightStyle.setLocked(true);
        // 自动换行
        alignRightStyle.setWrapText(false);
        return commonStyle;
    }

    private List<ThemeToResutl>  handResult(ThemeQueryModel themeQueryModel,List<Map<String, String>> data){
       List<String> fields = themeQueryModel.getFields();
        Map<String,String> fieldMap = themeQueryModel.getFieldMap();
        List<ThemeToResutl> list = new ArrayList<>();
        for (Map<String, String> map :data ) {
            ThemeToResutl result = ThemeToResutl.of();
            for (String field:fields) {
                if (CUST_NO.equals(field)){
                    result.setName(map.get(field));
                }else{
                    result.setFiledValue(map.get(fieldMap.get(field)));
                }
            }
            list.add(result);
        }
        return list;
    }


}
